package puzzle.slilin2.solver;

import java.util.List;

import puzzle.common.data.HatanException;
import puzzle.common.point.Pnt;
import puzzle.common.point.Vct;
import puzzle.common.solver.Track;
import puzzle.common.solver.TopologicalRule;
import puzzle.common.solver.TrackTopology;
import puzzle.slilin2.data.LineCellState;
import puzzle.slilin2.data.SlilinPntHelper;
import puzzle.slilin2.data.SlilinBoard;

class AvoidLoopTopologicalRule implements TopologicalRule<SlilinBoard, TrackTopology> {

	public boolean apply(SlilinBoard b, TrackTopology topology) throws HatanException {
		List<Track> tracks = topology.getTracks();

		if (tracks.size() <= 1) {
			return false;
		}

		boolean isChanged = false;
		for (Track track : tracks) {
			isChanged |= apply(b, track);
		}
		return isChanged;
	}

	public boolean apply(SlilinBoard b, Track track) {
		boolean isChanged = false;

		Pnt p0 = track.getEnds().car;
		Vct v = p0.vctTo(track.getEnds().cdr);
		int d = v.getManhattanDistance();
		if (d == 2) {
			Vct v1 = new Vct(v.dx / 2, v.dy / 2);
			LineCellState s = b.getLineCellState(p0.add(v1));
			if (!s.isFixed()) {
				isChanged |= s.setForbidden(true);
			}
		} else if (d == 4) {
			if (v.dx == 0 || v.dy == 0) {
				Vct v1 = new Vct(v.dx / 4, v.dy / 4);
				isChanged |= checkI(b, p0, v1);
			} else {
				Vct v1 = new Vct(v.dx / 2, 0);
				Vct v2 = new Vct(0, v.dy / 2);
				isChanged |= checkL(b, p0, v1, v2);
				isChanged |= checkL(b, p0, v2, v1);
			}
		}

		return isChanged;
	}

	private boolean checkI(SlilinBoard b, Pnt p0, Vct v) {
		return checkL(b, p0, v, v);
	}

	private boolean checkL(SlilinBoard b, Pnt p0, Vct v1, Vct v2) {
		Pnt p = p0.add(v1);
		LineCellState s1 = b.getLineCellState(p);
		if (s1.isFixed()) {
			return false;
		}

		if (isOnlyOneChoice(b, p, v1, v2)) {
			LineCellState s2 = b.getLineCellState(p.add(v1).add(v2));
			if (s2.isFixed()) {
				return false;
			}
			return s1.setForbidden(true) | s2.setForbidden(true);
		} else {
			return false;
		}
	}

	private boolean isOnlyOneChoice(SlilinBoard b, Pnt p0, Vct v1, Vct v2) {
		for (Pnt p : SlilinPntHelper.getLinePointsAroundDot(b, p0.add(v1))) {
			if (p0.equals(p) || p0.add(v1).add(v2).equals(p)) {
				continue;
			}
			if (!b.getLineCellState(p).isForbidden()) {
				return false;
			}
		}
		return true;
	}

}
